<template>
  <view v-for="(ball, index) in balls">
    <view :key="index" v-if="ball.visible" class="fly-ball" :animation="ball.animation"
      :style="{ left: `${ball.startX}px`, top: `${ball.startY}px` }" />
  </view>
</template>

<script setup>
import { ref } from 'vue'

// 可同时存在的最大飞行小球数
const balls = ref(Array.from({ length: 5 }, () => ({
  visible: false,
  animation: null,
  startX: 0,
  startY: 0,
  finishCallback: null
})))

// 启动飞行动画
const launch = (startRect, endRect, callback) => {
  if (!startRect || !endRect) return

  // 找到一个空闲的小球
  const ball = balls.value.find(b => !b.visible)
  if (!ball) return // 没有空闲球则忽略

  const sX = startRect.left
  const sY = startRect.top
  const eX = endRect.left
  const eY = endRect.top
  const cX = (sX + eX) / 2
  const cY = sY - 150

  Object.assign(ball, {
    startX: sX,
    startY: sY,
    visible: true,
    finishCallback: callback || null
  })

  const animation = uni.createAnimation({ duration: 0 })
  const steps = 40
  let i = 0

  const timer = setInterval(() => {
    if (i > steps) {
      clearInterval(timer)
      ball.visible = false
      if (typeof ball.finishCallback === 'function') {
        ball.finishCallback()
      }
      return
    }
    const t = i / steps
    const x = (1 - t) ** 2 * sX + 2 * (1 - t) * t * cX + t ** 2 * eX
    const y = (1 - t) ** 2 * sY + 2 * (1 - t) * t * cY + t ** 2 * eY
    animation.translate(x - sX, y - sY).step({ duration: 16 })
    ball.animation = animation.export()
    i++
  }, 16)
}

defineExpose({ launch })
</script>

<style scoped>
.fly-ball {
  width: 20rpx;
  height: 20rpx;
  background-color: red;
  border-radius: 50%;
  position: fixed;
  z-index: 9999;
  pointer-events: none;
}
</style>